/*
 * array_list.c
 *
 *  Created on: 2012-7-22
 *      Author: lin
 */

#include "array_list.h"

#define List array_list_t
#define Iter array_list_iter_t

static void _expend_capacity(List* list, int32_t expect) {
    int32_t capacity = list->capacity;
    while (capacity < expect) {
        capacity <<= 1;
    }
    array_list_set_capacity(list, capacity);
}

List* array_list_new0(int32_t capacity) {
    assert(capacity > 0);
    mixed_t* array = zmalloc(sizeof(mixed_t) * capacity);
    List * list = zcalloc(sizeof(List));
    list->array = array;
    list->capacity = capacity;
    return list;
}

List* array_list_clean(List *list) {
    int32_t size = list->size;
    list->size = 0;
    mixed_t_free_fn freefn = list->free_fn;
    if (freefn && size) {
        mixed_t* array = list->array;
        for (int32_t i = 0; i < size;) {
            freefn(array + i++);
        }
    }
    return list;
}
void array_list_free(List *list) {
    array_list_clean(list);
    zfree(list->array);
    zfree(list);
}

List* array_list_set_capacity(array_list_t* list, int32_t newcapacity) {
    assert(list->size <= newcapacity);
    mixed_t* array = zrealloc(list->array, sizeof(mixed_t) * newcapacity);
    list->array = array;
    list->capacity = newcapacity;
    return list;
}

List* array_list_put(List *list, const mixed_t val) {
    int32_t size = list->size++;
    if (size == list->capacity) {
        _expend_capacity(list, size + 1);
    }
    *(list->array + size) = val;
    return list;
}
List* array_list_set(List *list, int32_t idx, const mixed_t value) {
    assert(idx >= 0);
    assert(idx < list->size);
    *(list->array + idx) = value;
    return list;
}

int32_t array_list_index(const List *list, const mixed_t match) {
    int32_t size = list->size;
    if (!size) {
        return -1;
    }
    mixed_t_match_fn matchfn = list->match_fn;
    mixed_t* array = list->array;
    if (matchfn) {
        for (int32_t i = 0; i < size; i++) {
            if (matchfn(array[i], match)) {
                return i;
            }
        }
        return -1;
    }
    for (int32_t i = 0; i < size; i++) {
        if (array[i].s64 == match.s64) {
            return i;
        }
    }
    return -1;
}
mixed_t array_list_get(const List *list, int32_t idx) {
    assert(idx >= 0);
    assert(idx < list->size);
    return list->array[idx];
}
List* array_list_remove(List *list, int32_t idx) {
    assert(idx >= 0);
    assert(idx < list->size);
    int32_t size = --list->size;
    mixed_t* array = list->array;
    mixed_t_free_fn freefn = list->free_fn;
    if (freefn) {
        freefn(array[idx]);
    }
    if (idx != size) {
        memmove(array + idx, array + idx + 1, size - 1);
    }
    return list;
}

Iter *array_list_iter_new(const List *list, int32_t reverse) {
    Iter * iter = zmalloc(sizeof(Iter));
    array_list_rewind(list, iter, reverse);
    return iter;
}
mixed_t array_list_iter_next(Iter *iter, int* hasnext) {
    List* list = iter->list;
    int32_t idx;
    mixed_t* array = list->array;
    mixed_t val;
    *hasnext = 1;
    if (iter->reverse) {
        idx = iter->idx--;
        return idx < 0 ? (*hasnext = 0, val) : array[idx];
    } else {
        idx = iter->idx++;
        return idx >= list->size ? (*hasnext = 0, val) : array[idx];
    }
}
List *array_list_dup(const List *orig) {
    List* newlist = array_list_new0(orig->capacity);
    mixed_t_dup_fn dupfn = orig->dup_fn;
    int32_t size = orig->size;
    newlist->dup_fn = dupfn;
    newlist->free_fn = orig->free_fn;
    newlist->match_fn = orig->match_fn;

    mixed_t* origarray = orig->array;
    if (!dupfn) {
        memcpy(newlist->array, origarray, size);
        newlist->size = size;
    } else {
        for (int32_t i = 0; i < size; i++) {
            mixed_t copy;
            if (!dupfn(origarray[i], &copy)) {
                array_list_put(newlist, copy);
            } else {
                array_list_free(newlist);
                return NULL;
            }
        }
    }
    return newlist;
}

int32_t array_list_to_array(const List *list, mixed_t* array, int32_t sizeto) {
    int32_t size = list->size;
    if (sizeto > size) {
        sizeto = size;
    }
    memcpy(array, list->array, sizeto);
    return sizeto;
}

#undef List
#undef Iter
